package blazingbubble.config;

import org.apache.log4j.Logger;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import java.net.InetAddress;
import java.net.UnknownHostException;

import blazingbubble.config.exception.ConfigReaderException;
import spark.math.Vector2;

public class ConfigReader {
	private static Logger logger = Logger.getLogger(ConfigReader.class);

	private static final String PROPERTY_SERVER_UDP_PORT = "serverUdpPort";
	private static final String PROPERTY_SERVER_ADDRESS = "serverAddress";
	private static final String PROPERTY_FRAME_BREAK = "frameBreak";
	private static final int MULTIPLAYER_MAX_PLAYERS_COUNT = 10;
	private static final String PROPERTY_MULTIPLAYER_PREFIX = "players.";
	private static final String PROPERTY_MULTIPLAYER_SPRITEMAP = "spriteMap";
	private static final String PROPERTY_MULTIPLAYER_POS_X = ".position.x";
	private static final String PROPERTY_MULTIPLAYER_POS_Y = ".position.y";
	private static final String PROPERTY_MULTIPLAYER_SCALE = ".scale";

	public Config readConfig(String configFilePath, String multiplayerIniPath) throws ConfigReaderException {
		//create an input stream from the specified location of the config file
		FileInputStream fileInputStream = null;
		try {
			fileInputStream = new FileInputStream(configFilePath);
		}
		catch(FileNotFoundException e) {
			throw new ConfigReaderException(e);
		}

		//create properties on the config file stream
		Properties properties = new Properties();
		try {
			properties.load(fileInputStream);
		}
		catch(IOException e) {
			throw new ConfigReaderException(e);
		}

		//load config values
		Config config = new Config();

		config.setServerAddress(readServerAddress(properties));
		config.setServerUdpPort(readServerUdpPort(properties));
		config.setFrameBreak(readFrameBreak(properties));

		//create an input stream from the specified location of the multiplayer ini file
		try {
			fileInputStream = new FileInputStream(multiplayerIniPath);
		}
		catch(FileNotFoundException e) {
			throw new ConfigReaderException(e);
		}

		//create properties on the config file stream
		try {
			properties.load(fileInputStream);
		}
		catch(IOException e) {
			throw new ConfigReaderException(e);
		}

		readMultiplayerFieldInformation(config, properties);

		return config;
	}

	private void readMultiplayerFieldInformation(Config config, Properties properties) throws ConfigReaderException {
		for(int field = 0; field <= MULTIPLAYER_MAX_PLAYERS_COUNT; field++) {
			for(int fieldPlayers = 0; fieldPlayers <= MULTIPLAYER_MAX_PLAYERS_COUNT; fieldPlayers++) {
				MultiplayerFieldInformation multiplayerFieldInformation = new MultiplayerFieldInformation();
				String prefix = PROPERTY_MULTIPLAYER_PREFIX + field + fieldPlayers + ".";
				String spriteMap = properties.getProperty(prefix + PROPERTY_MULTIPLAYER_SPRITEMAP);
				// if there is no entry for this player count just continue
				if(spriteMap == null) {
					continue;
				}
				multiplayerFieldInformation.setSpriteMap(spriteMap);
				for(int currentPlayerPosition = 0; currentPlayerPosition < field; currentPlayerPosition++) {
					try {
						Vector2 position = new Vector2(Float.parseFloat(properties.getProperty(prefix + currentPlayerPosition + PROPERTY_MULTIPLAYER_POS_X)),
						                               Float.parseFloat(properties.getProperty(prefix + currentPlayerPosition + PROPERTY_MULTIPLAYER_POS_Y)));
						Float scale = Float.parseFloat(properties.getProperty(prefix + currentPlayerPosition + PROPERTY_MULTIPLAYER_SCALE));
						multiplayerFieldInformation.getFieldInformation().put(currentPlayerPosition, new MultiplayerFieldInformation.FieldInformation(position, scale));
					}
					catch(NumberFormatException e) {
						throw new ConfigReaderException("Multiplayer information on player count " + field + " for player " + currentPlayerPosition + " is invalid (position or scale value is not a floating point");
					}
				}
				config.getMultiplayerFieldInformation().put("" + field + fieldPlayers, multiplayerFieldInformation);
			}
		}
	}

	private Integer readFrameBreak(Properties properties) throws ConfigReaderException {
		String frameBreak = properties.getProperty(PROPERTY_FRAME_BREAK);

		if(frameBreak != null) {
			try {
				return Integer.parseInt(frameBreak);
			}
			catch(NumberFormatException e) {
				throw new ConfigReaderException("Invaild value on " + PROPERTY_FRAME_BREAK + ": " + frameBreak);
			}
		}
		else {
			return null;
		}
	}

	private InetAddress readServerAddress(Properties properties) throws ConfigReaderException {
		String address = properties.getProperty(PROPERTY_SERVER_ADDRESS);

		if(address != null) {
			try {
				return InetAddress.getByName(address);
			}
			catch(UnknownHostException e) {
				throw new ConfigReaderException("Invaild host address value on " + PROPERTY_SERVER_ADDRESS + ": " + address, e);
			}
		}
		else {
			return null;
		}
	}

	private Integer readServerUdpPort(Properties properties) throws ConfigReaderException {
		String port = properties.getProperty(PROPERTY_SERVER_UDP_PORT);

		if(port != null) {
			try {
				return Integer.parseInt(port);
			}
			catch(NumberFormatException e) {
				throw new ConfigReaderException("Invaild port value on " + PROPERTY_SERVER_UDP_PORT + ": " + port);
			}
		}
		else {
			return null;
		}
	}
}